package com.example.data.factory.service.service.Impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.data.factory.common.Dto.*;
import com.example.data.factory.common.common.StringConstant;
import com.example.data.factory.common.result.PageVO;
import com.example.data.factory.common.result.R;
import com.example.data.factory.common.util.CommonUtil;
import com.example.data.factory.common.util.ParamParser;
import com.example.data.factory.entity.entity.Classify;
import com.example.data.factory.entity.entity.Script;
import com.example.data.factory.entity.entity.ScriptParam;
import com.example.data.factory.mapper.mapper.ClassifyMapper;
import com.example.data.factory.mapper.mapper.ScriptMapper;
import com.example.data.factory.service.service.ScriptParamService;
import com.example.data.factory.service.service.ScriptService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.time.LocalDateTime;
import java.util.*;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author lhy
 * @since 2025-03-03
 */
@Service
public class ScriptServiceImpl extends ServiceImpl<ScriptMapper, Script> implements ScriptService {

    @Override
    public R<Script> search(SearchScriptDto searchScriptDto) {
        // 创建分页对象，使用前端传来的页码和每页大小
        Page<Script> page = new Page<> ( searchScriptDto.getPageNumber (), searchScriptDto.getPageSize () );

        // 构建查询条件
        LambdaQueryWrapper<Script> lambdaQueryWrapper = new LambdaQueryWrapper<> ();
        lambdaQueryWrapper.eq ( Script::getDeleted, 0 ); // 默认过滤掉已删除的数据

        // 动态添加查询条件
        if (searchScriptDto.getStatus () != null) {
            lambdaQueryWrapper.eq ( Script::getStatus, searchScriptDto.getStatus () );
        }
        if (StringUtils.isNotBlank ( searchScriptDto.getScriptName () )) {
            lambdaQueryWrapper.like ( Script::getScriptName, searchScriptDto.getScriptName () );
        }

        // 执行查询
        Page<Script> pageResult = this.page ( page, lambdaQueryWrapper );

        // 将查询结果转换为PageVO类型，以便于返回给前端
        PageVO<Script> pageVO = new PageVO<> ();
        BeanUtil.copyProperties ( pageResult, pageVO ); // 确保BeanUtil能正确复制属性

        return R.Success ( pageVO );
    }

    private static final Logger logger = LoggerFactory.getLogger ( InterfaceServiceImpl.class );
    @Resource
    private ScriptParamService scriptParamService;
    @Resource
    private CommonUtil commonUtil;
    @Resource
    private ClassifyMapper classifyMapper;
    @Resource
    private ScriptMapper scriptMapper;

    /**
     * 根据分类名称递归获取父级路径
     */
    public String buildCategoryPath(Long id, int maxDepth) {
        StringBuilder fullPath = new StringBuilder ();
        Classify currentCategory = classifyMapper.selectById ( id );
        int depth = 0;

        while (currentCategory != null && depth < maxDepth) {
            if (fullPath.length () > 0) {
                fullPath.insert ( 0, "/" );
            }
            fullPath.insert ( 0, currentCategory.getName () );
            if (currentCategory.getParentId () != null && !currentCategory.getParentId ().equals ( 0L )) {
                currentCategory = classifyMapper.selectById ( currentCategory.getParentId () );
                depth++;
            } else {
                break;
            }
        }

        if (depth >= maxDepth) {
            log.warn ( "分类层级过深，可能存在问题" );
        }

        return fullPath.toString ();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<Script> insert(InsertScriptDto insertScriptDto, HttpServletRequest request, MultipartFile file) {
        // 校验分类名称
        String classifyName = insertScriptDto.getClassifyName ();
        if (StringUtils.isBlank ( classifyName )) {
            return R.Failed ( "无效的分类名称" );
        }

        try {
            // 根据分类名称找到分类信息
            Classify classify = classifyMapper.selectBynames ( classifyName );
            if (classify == null) {
                return R.Failed ( "没有该分类" );
            }

            // 检查脚本名称是否重复
            boolean isScriptNameExists = this.lambdaQuery ()
                    .eq ( Script::getScriptName, insertScriptDto.getScriptName () )
                    .exists ();
            if (isScriptNameExists) {
                return R.Failed ( StringConstant.ERRO );
            }

           //  校验并处理上传的 Python 文件
            if (file == null || file.isEmpty ()) {
                return R.Failed ( "请上传一个有效的 Python 文件" );
            }
            String fileName = file.getOriginalFilename ();
            if (fileName == null || !fileName.toLowerCase ().endsWith ( ".py" )) {
                return R.Failed ( "文件类型错误，仅支持上传 .py 文件" );
            }

            // 定义文件存储路径
            String uploadDirPath = System.getProperty ( "user.dir" ) + File.separator + "uploads";
            File uploadDir = new File ( uploadDirPath );
            if (!uploadDir.exists ()) {
                uploadDir.mkdirs ();
            }

            // 生成唯一文件名
            String uniqueFileName = UUID.randomUUID ().toString () + "_" + fileName;
            String filePath = uploadDir.getAbsolutePath () + File.separator + uniqueFileName;

            // 保存文件
            try {
                file.transferTo ( new File ( filePath ) );
            } catch (Exception e) {
                log.error ( "文件保存失败", e );
                return R.Failed ( "文件保存失败，请稍后再试" );
            }

            // 设置文件路径到 DTO
            insertScriptDto.setScriptFileUrl ( filePath );

            // 新增脚本
            Script script = new Script ();
            BeanUtil.copyProperties ( insertScriptDto, script );
            String categoryPath = buildCategoryPath ( Long.valueOf ( classify.getId () ), 10 ); // 最大深度为10
            script.setClassifyId ( classify.getId () );
            script.setClassifyName ( categoryPath );
            script.setCreateBy ( commonUtil.extractUsernameFromRequest ( request ) );
            script.setUpdateTime ( LocalDateTime.now () );
            script.setCreateTime ( LocalDateTime.now () );

            boolean save = this.save ( script );
            if (!save) {
                throw new RuntimeException ( "保存脚本信息失败" );
            }
            // 获取当前新增脚本的 ID
            Integer scriptId = script.getScriptId ();
            saveInterfaceParam ( scriptId, insertScriptDto.getInputParams (), (byte) 1 );
            saveInterfaceParam ( scriptId, insertScriptDto.getOutputParams (), (byte) 2 );

            return R.Success ( StringConstant.INSERTSUCCESS );
        } catch (Exception e) {
            log.error ( "插入脚本信息失败", e );
            throw new RuntimeException ( e );
        }
    }
    /**
     * 保存输入/输出参数
     */
    private void saveInterfaceParam(Integer interfaceId, Object value, byte paramType) {
        if (value == null) {
            return;
        }

        try {
            ScriptParam scriptParam = new ScriptParam ();
            scriptParam.setScriptId ( interfaceId );
            scriptParam.setValue ( value instanceof String ? (String) value : value.toString () );
            scriptParam.setParamType ( (int) paramType );
            scriptParam.setUpdateTime ( LocalDateTime.now () );
            scriptParam.setCreateTime ( LocalDateTime.now () );
            boolean saved = scriptParamService.save ( scriptParam );

            if (!saved) {
                throw new RuntimeException ( "保存参数失败" );
            }
        } catch (Exception e) {
            log.error ( "保存参数失败，scriptId: {}, paramType: {}" );
            throw new RuntimeException ( "保存参数失败：" + e.getMessage (), e );
        }
    }

    @Override
    public R<Script> updatestatus(UpdateScriptStatusDto updateScriptStatusDto) {
        //先查询该条数据，如果被删除则不能修改
        Script one = this.getOne ( new LambdaQueryWrapper<Script> ().eq ( Script::getScriptId, updateScriptStatusDto.getScriptId () ) );
        if (one == null || Objects.isNull ( one )) {
            return R.Failed ( "找不到该脚本" );
        }
        if (one.getDeleted () == 1) {
            return R.Failed ( "该数据已经被删除，无法进行更改" );
        }
        Script script = new Script ();
        script.setUpdateTime ( LocalDateTime.now () );
        boolean update = this.update ( new LambdaUpdateWrapper<Script> ().set ( Script::getStatus, updateScriptStatusDto.getStatus () ).eq ( Script::getScriptId, updateScriptStatusDto.getScriptId () ) );
        if (update) {
            return R.Success ( StringConstant.UPDATESUCCESS );
        }
        return R.Failed ( StringConstant.UPDATEFAILED );
    }

    @Override
    public R<Script> updatestatusbash(List<UpdateScriptStatusDto> updateScriptStatusDtos) {
        if (updateScriptStatusDtos == null || updateScriptStatusDtos.isEmpty ()) {
            return R.Failed ( "输入数据为空" );
        }
        boolean allUpdateSuccess = true;
        for (UpdateScriptStatusDto dto : updateScriptStatusDtos) {
            Script one = this.getOne ( new LambdaQueryWrapper<Script> ().eq ( Script::getScriptId, dto.getScriptId () )
                    .eq ( Script::getDeleted, 0 ) );
            if (one == null) {
                allUpdateSuccess = false;
                continue;
            }
            Script scripts = new Script ();
            scripts.setUpdateTime ( LocalDateTime.now () );
            //执行更新
            boolean update = this.update ( new LambdaUpdateWrapper<Script> ().set ( Script::getStatus, dto.getStatus () ).eq ( Script::getScriptId, dto.getScriptId () ) );
            if (update) {
                allUpdateSuccess = true;
            }
        }
        if (!allUpdateSuccess) {
            return R.Failed ( "部分未更新成功" );
        }
        return R.Success ( StringConstant.UPDATESUCCESS );
    }

    @Override
    public R<Script> delete(DeleteScriptDto deleteScriptDto) {
        // 根据scriptId查询脚本，确保只获取未被删除的记录
        Script one = this.getOne ( new LambdaQueryWrapper<Script> ()
                .eq ( Script::getScriptId, deleteScriptDto.getScriptId () )
                .eq ( Script::getDeleted, 0 ) );

        if (one == null) {
            return R.Failed ( "找不到该数据" );
        }

        if (!(one.getStatus () == 0)) {
            return R.Failed ( StringConstant.UPDATEERRO );
        }

        // 设置要更新的脚本信息，包括ID和deleted状态
        Script scriptToUpdate = new Script ();
        scriptToUpdate.setScriptId ( one.getScriptId () );// 确保设置了正确的ID
        scriptToUpdate.setDeleted ( (byte) 1 );

        boolean updateResult = this.updateById ( scriptToUpdate );

        if (updateResult) {
            return R.Success ( StringConstant.UPDATESUCCESS );
        } else {
            return R.Failed ( StringConstant.UPDATEFAILED );
        }
    }

    @Override
    public R<Script> searchbyid(DeleteScriptDto deleteScriptDto) {
        Script one = this.getOne ( new LambdaQueryWrapper<Script> ().eq ( Script::getScriptId, deleteScriptDto.getScriptId () ).eq ( Script::getDeleted, 0 ) );
        if (Objects.isNull ( one )) {
            return R.Failed ( "找不到该条数据" );
        }
        return R.Success ( one );
    }

    @Override
    public R<Script> updates(UpdateScriptDto updateScriptDto, MultipartFile file) {
        // 根据 ID 查询当前更新的数据
        Script one = this.getOne ( new LambdaQueryWrapper<Script> ().eq ( Script::getScriptId, updateScriptDto.getScriptId () ) );
        if (Objects.isNull ( one )) {
            return R.Failed ( "未找到该数据" );
        }
        if (one.getStatus () == 1) {
            return R.Failed ( StringConstant.UPDATEERRO );
        }

        // 检查脚本名称是否重复
        boolean isScriptNameExists = this.lambdaQuery ()
                .eq ( Script::getScriptName, updateScriptDto.getScriptName () )
                .ne ( Script::getScriptId, updateScriptDto.getScriptId () )
                .exists ();
        if (isScriptNameExists) {
            return R.Failed ( StringConstant.ERRO );
        }

        // 更新脚本基本信息
        boolean update = this.update ( new LambdaUpdateWrapper<Script> ()
                .set ( Script::getScriptName, updateScriptDto.getScriptName () )
                .set ( Script::getClassifyName, updateScriptDto.getClassifyName () )
                .set ( Script::getDescription, updateScriptDto.getDescription () )
                .set ( Script::getFunctionName, updateScriptDto.getFunctionName () )
                .set ( Script::getInputParams, updateScriptDto.getInputParams () )
                .set ( Script::getOutputParams, updateScriptDto.getOutputParams () )
                .set ( Script::getClassName, updateScriptDto.getClassName () )
                .set ( Script::getScriptType, updateScriptDto.getScriptType () )
                .eq ( Script::getScriptId, updateScriptDto.getScriptId () ) );
        if (!update) {
            return R.Failed ( "更新失败" );
        }

        // 如果上传了文件，则处理文件替换逻辑
        if (file != null && !file.isEmpty ()) {
            // 校验脚本状态：仅允许未发布状态的脚本上传文件
            if (!(one.getStatus () == 0)) {
                return R.Failed ( "仅未发布状态的脚本可以上传文件" );
            }

            // 检查文件类型
            String fileName = file.getOriginalFilename ();
            if (fileName == null || !fileName.toLowerCase ().endsWith ( ".py" )) {
                return R.Failed ( "文件类型错误，仅支持上传 .py 文件" );
            }

            // 定义文件存储路径
            String uploadDirPath = System.getProperty ( "user.dir" ) + File.separator + "uploads";
            File uploadDir = new File ( uploadDirPath );
            if (!uploadDir.exists ()) {
                uploadDir.mkdirs ();
            }

            // 生成唯一文件名
            String uniqueFileName = UUID.randomUUID ().toString () + "_" + fileName;
            String filePath = uploadDir.getAbsolutePath () + File.separator + uniqueFileName;

            // 保存文件
            try {
                file.transferTo ( new File ( filePath ) );
            } catch (Exception e) {
                log.error ( "文件保存失败", e );
                return R.Failed ( "文件保存失败，请稍后再试" );
            }

            // 更新脚本文件路径
            one.setScriptFileUrl ( filePath );
            this.updateById ( one );
        }

        // 更新分类信息
        String classifyName = updateScriptDto.getClassifyName ();
        if (classifyName == null || classifyName.trim ().isEmpty ()) {
            return R.Failed ( "无效的分类名称" );
        }

        // 查询分类信息
        Classify classify = classifyMapper.selectBynames ( classifyName );
        if (classify == null) {
            return R.Failed ( "没有找到该分类" );
        }

        // 确保分类 ID 不为空
        Integer classifyId = classify.getId ();
        if (classifyId == null) {
            return R.Failed ( "分类 ID 为空，请检查分类数据" );
        }

        // 构建分类路径
        String categoryPath = buildCategoryPath ( Long.valueOf ( classifyId ), 10 );

        // 更新脚本分类信息
        Script script = new Script ();
        script.setScriptId ( updateScriptDto.getScriptId () );
        script.setClassifyId ( classifyId );
        script.setClassifyName ( categoryPath );
        script.setUpdateTime ( LocalDateTime.now () );
        this.updateById ( script );

        // 更新输入输出参数
        Integer scriptId = updateScriptDto.getScriptId ();
        saveInterfaceParam ( scriptId, updateScriptDto.getInputParams (), (byte) 1 );
        saveInterfaceParam ( scriptId, updateScriptDto.getOutputParams (), (byte) 2 );

        return R.Success ( StringConstant.UPDATESUCCESS );
    }

    @Override
    public R<Map<String, String>> updatesBatch(ScriptClassifyBatch scriptClassifyBatch) {
        //判断传入的接口名称集合是否是表中已经存在的
        List<String> scriptNames = scriptClassifyBatch.getScriptName ();
        String classifyName = scriptClassifyBatch.getClassifyName ();
        if (scriptNames == null || scriptNames.isEmpty () || classifyName == null || classifyName.trim ().isEmpty ()) {
            return R.Failed ( "未输入有效的数据" );
        }
        Map<String, String> results = new HashMap<> ();
        Classify classify = classifyMapper.selectBynames ( classifyName );
        if (classify == null) {
            for (String scriptName : scriptNames) {
                results.put ( scriptName, "没有找到分类" );
            }
            return R.Failed ( results.toString () );
        }
        String s = buildCategoryPath ( Long.valueOf ( classify.getId () ), 10 );
        for (String scriptName : scriptNames) {
            Script one = this.getOne ( new LambdaQueryWrapper<Script> ().eq ( Script::getScriptName, scriptName ) );
            if (one == null) {
                results.put ( scriptName, "脚本不存在" );
                continue;
            }
            boolean update = this.update ( new LambdaUpdateWrapper<Script> ().set ( Script::getClassifyName, s )
                    .set ( Script::getClassifyId, classify.getId () )
                    .set ( Script::getUpdateTime, LocalDateTime.now () )
                    .eq ( Script::getScriptId, one.getScriptId () ) );
            if (!update) {
                results.put ( scriptName, "更新失败" );
            } else {
                results.put ( scriptName, "更新成功" );
            }
        }
        long successCount = results.values ().stream ().filter ( result -> result.equals ( "更新成功" ) ).count ();
        long failedCount = results.size () - successCount;
        if (failedCount > 0) {
            return R.Failed ( results.toString () );
        } else {
            return R.Success ( StringConstant.UPDATESUCCESS );
        }
    }

    public R<String> testScriptById(Integer scriptId, String inputData) {
        // 1. 根据 ID 查询脚本信息
        Script script = this.getById ( scriptId );
        if (script == null) {
            return R.Failed ( "未找到该脚本" );
        }
        // 2. 获取脚本文件路径
        String scriptFileUrl = script.getScriptFileUrl ();
        if (StringUtils.isBlank ( scriptFileUrl )) {
            return R.Failed ( "脚本文件路径为空，无法测试" );
        }

        File scriptFile = new File ( scriptFileUrl );
        if (!scriptFile.exists ()) {
            return R.Failed ( "脚本文件不存在，请检查文件路径" );
        }
        String wrappedInputData = inputData;

        // 4. 测试 Python 文件并传递输入数据
        String testResult = testUploadedPythonFile ( scriptFileUrl, wrappedInputData );
        if (testResult.contains ( "失败" )) {
            return R.Failed ( "Python 文件测试失败: " + testResult );
        }

        return R.Success ( "测试成功，输出结果:\n" + testResult );
    }

    /**
     * 测试上传的 Python 文件
     *
     * @param filePath  Python 文件路径
     * @param inputData 输入数据（JSON 格式）
     * @return 测试结果
     */
    private String testUploadedPythonFile(String filePath, String inputData) {
        StringBuilder output = new StringBuilder ();

        try {
            // 构建命令
            ProcessBuilder processBuilder = new ProcessBuilder ( "python", filePath );
            processBuilder.redirectErrorStream ( true );

            // 启动进程
            Process process = processBuilder.start ();

            // 向 Python 脚本的标准输入写入数据
            try (BufferedWriter writer = new BufferedWriter ( new OutputStreamWriter ( process.getOutputStream () ) )) {
                writer.write ( inputData ); // 写入用户提供的 JSON 数据
                writer.newLine (); // 写入换行符以结束输入
                writer.flush ();
            }

            // 捕获标准输出和错误输出
            try (BufferedReader reader = new BufferedReader ( new InputStreamReader ( process.getInputStream () ) )) {
                String line;
                while ((line = reader.readLine ()) != null) {
                    output.append ( line ).append ( "\n" );
                }
            }

            // 等待进程结束
            int exitCode = process.waitFor ();
            if (exitCode != 0) {
                return "执行失败，退出码: " + exitCode + "\n详细输出:\n" + output.toString ();
            }
        } catch (Exception e) {
            log.error ( "Python 文件测试失败", e );
            return "测试失败: " + e.getMessage ();
        }

        return output.toString (); // 返回执行结果
    }

    @Override
    public Map<String, String> testInterfaces(Integer scriptId) {
        // 从数据库加载接口信息
        Script iface = scriptMapper.selectById ( scriptId );
        if (iface == null) {
            throw new RuntimeException ( "脚本信息不存在" );
        }

        // 解析输入参数
        try {
            return ParamParser.parseInputParams ( String.valueOf ( iface.getInputParams () ) );
        } catch (Exception e) {
            throw new RuntimeException ( "参数解析失败", e );
        }
    }
}
